/*
 * @Author: xxx
 * @Date: 2024-07-10 15:07:34
 * @LastEditors: xxx
 * @LastEditTime: 2024-07-11 16:05:36
 * @FilePath: \postgresqltest\geometry\geometry.go
 * @Description: 正式版
 */
package geometry

import (
	"context"
	"database/sql"
	"database/sql/driver"
	"encoding/hex"
	"fmt"
	"reflect"

	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

type Geometry[T any] struct {
	Data *T
}

func NewGeometry[T any](g T) *Geometry[T] {
	return &Geometry[T]{
		Data: &g,
	}
}

func (g *Geometry[T]) Scan(src any) error {
	var t T
	g.Data = &t
	return scan(g.Data, src)
}

func (g *Geometry[T]) GormValue(ctx context.Context, db *gorm.DB) (expr clause.Expr) {
	return gormValue(g.Data, db)
}

func (g *Geometry[T]) GormDataType() string {
	return "geometry"
}

func scan(scn, src any) error {
	str, ok := src.(string)
	if !ok {
		return fmt.Errorf("rdb: want string, got %T", reflect.TypeOf(src))
	}
	data, err := hex.DecodeString(str)
	if err != nil {
		return err
	}

	sn, ok := scn.(sql.Scanner)
	if !ok {
		return fmt.Errorf("rdb: sql.Scanner, got %T", reflect.TypeOf(scn))
	}

	return sn.Scan(data)
}

func gormValue(vr any, db *gorm.DB) (expr clause.Expr) {
	val, ok := vr.(driver.Valuer)
	if !ok {
		db.AddError(fmt.Errorf("rdb: sql.Scanner, got %T", reflect.TypeOf(vr)))
	}

	dv, err := val.Value()
	if err != nil {
		db.AddError(err)
		return
	}

	data, ok := dv.([]byte)
	if !ok {
		db.AddError(fmt.Errorf("rdb: want  []byte, got %T", dv))
		return
	}

	byteaStr := fmt.Sprintf("\\\\x%s", hex.EncodeToString(data))
	expr = clause.Expr{
		SQL:  "ST_GeomFromEWKB(E'" + byteaStr + "')",
		Vars: nil,
	}

	return
}
